package com.choice.wechat.util.alipay.util;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.httpclient.NameValuePair;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;

import com.choice.wechat.util.alipay.config.AlipayConfig;
import com.choice.wechat.util.alipay.util.httpClient.HttpProtocolHandler;
import com.choice.wechat.util.alipay.util.httpClient.HttpRequest;
import com.choice.wechat.util.alipay.util.httpClient.HttpResponse;
import com.choice.wechat.util.alipay.util.httpClient.HttpResultType;
import com.choice.wechat.util.alipay.sign.MD5;

/* *
 *类名：AlipaySubmit
 *功能：支付宝各接口请求提交类
 *详细：构造支付宝各接口表单HTML文本，获取远程HTTP数据
 *版本：3.3
 *日期：2012-08-13
 *说明：
 *以下代码只是为了方便商户测试而提供的样例代码，商户可以根据自己网站的需要，按照技术文档编写,并非一定要使用该代码。
 *该代码仅供学习和研究支付宝接口使用，只是提供一个参考。
 */

public class AlipaySubmit {
    
    /**
     * 支付宝提供给商户的服务接入网关URL(新)
     */
    private static final String ALIPAY_GATEWAY_NEW = "https://mapi.alipay.com/gateway.do?";
	
    /**
     * 生成签名结果
     * @param sPara 要签名的数组
     * @return 签名结果字符串
     */
	public static String buildRequestMysign(Map<String, String> sPara) {
    	String prestr = AlipayCore.createLinkString(sPara); //把数组所有元素，按照“参数=参数值”的模式用“&”字符拼接成字符串
        String mysign = "";
        if(AlipayConfig.sign_type.equals("MD5") ) {
        	mysign = MD5.sign(prestr, AlipayConfig.key, AlipayConfig.input_charset);
        }
        return mysign;
    }
	
    /**
     * 生成要请求给支付宝的参数数组
     * @param sParaTemp 请求前的参数数组
     * @return 要请求的参数数组
     */
    private static Map<String, String> buildRequestPara(Map<String, String> sParaTemp) {
        //除去数组中的空值和签名参数
        Map<String, String> sPara = AlipayCore.paraFilter(sParaTemp);
        //生成签名结果
        String mysign = buildRequestMysign(sPara);

        //签名结果与签名方式加入请求提交参数组中
        sPara.put("sign", mysign);
        sPara.put("sign_type", AlipayConfig.sign_type);

        return sPara;
    }

    /**
     * 建立请求，以表单HTML形式构造（默认）
     * @param sParaTemp 请求参数数组
     * @param strMethod 提交方式。两个值可选：post、get
     * @param strButtonName 确认按钮显示文字
     * @return 提交表单HTML文本
     */
    public static String buildRequest(Map<String, String> sParaTemp, String strMethod, String strButtonName) {
        //待请求参数数组
        Map<String, String> sPara = buildRequestPara(sParaTemp);
        List<String> keys = new ArrayList<String>(sPara.keySet());

        StringBuffer sbHtml = new StringBuffer();

        sbHtml.append("<form id=\"alipaysubmit\" name=\"alipaysubmit\" action=\"" + ALIPAY_GATEWAY_NEW
                      + "_input_charset=" + AlipayConfig.input_charset + "\" method=\"" + strMethod
                      + "\">");

        for (int i = 0; i < keys.size(); i++) {
            String name = (String) keys.get(i);
            String value = (String) sPara.get(name);

            sbHtml.append("<input type=\"hidden\" name=\"" + name + "\" value=\"" + value + "\"/>");
        }

        //submit按钮控件请不要含有name属性
        sbHtml.append("<input type=\"submit\" value=\"" + strButtonName + "\" style=\"display:none;\"></form>");
        sbHtml.append("<script>document.forms['alipaysubmit'].submit();</script>");

        return sbHtml.toString();
    }
    
    /**
     * 建立请求，以表单HTML形式构造，带文件上传功能
     * @param sParaTemp 请求参数数组
     * @param strMethod 提交方式。两个值可选：post、get
     * @param strButtonName 确认按钮显示文字
     * @param strParaFileName 文件上传的参数名
     * @return 提交表单HTML文本
     */
    public static String buildRequest(Map<String, String> sParaTemp, String strMethod, String strButtonName, String strParaFileName) {
        //待请求参数数组
        Map<String, String> sPara = buildRequestPara(sParaTemp);
        List<String> keys = new ArrayList<String>(sPara.keySet());

        StringBuffer sbHtml = new StringBuffer();

        sbHtml.append("<form id=\"alipaysubmit\" name=\"alipaysubmit\"  enctype=\"multipart/form-data\" action=\"" + ALIPAY_GATEWAY_NEW
                      + "_input_charset=" + AlipayConfig.input_charset + "\" method=\"" + strMethod
                      + "\">");

        for (int i = 0; i < keys.size(); i++) {
            String name = (String) keys.get(i);
            String value = (String) sPara.get(name);

            sbHtml.append("<input type=\"hidden\" name=\"" + name + "\" value=\"" + value + "\"/>");
        }
        
        sbHtml.append("<input type=\"file\" name=\"" + strParaFileName + "\" />");

        //submit按钮控件请不要含有name属性
        sbHtml.append("<input type=\"submit\" value=\"" + strButtonName + "\" style=\"display:none;\"></form>");

        return sbHtml.toString();
    }
    
    /**
     * 建立请求，以模拟远程HTTP的POST请求方式构造并获取支付宝的处理结果
     * 如果接口中没有上传文件参数，那么strParaFileName与strFilePath设置为空值
     * 如：buildRequest("", "",sParaTemp)
     * @param strParaFileName 文件类型的参数名
     * @param strFilePath 文件路径
     * @param sParaTemp 请求参数数组
     * @return 支付宝处理结果
     * @throws Exception
     */
    public static String buildRequest(String strParaFileName, String strFilePath, Map<String, String> sParaTemp) throws Exception {
        //待请求参数数组
        Map<String, String> sPara = buildRequestPara(sParaTemp);
        HttpProtocolHandler httpProtocolHandler = HttpProtocolHandler.getInstance();

        HttpRequest request = new HttpRequest(HttpResultType.BYTES);
        //设置编码集
        request.setCharset(AlipayConfig.input_charset);

        request.setParameters(generatNameValuePair(sPara));
        request.setUrl(ALIPAY_GATEWAY_NEW+"_input_charset="+AlipayConfig.input_charset);

        HttpResponse response = httpProtocolHandler.execute(request,strParaFileName,strFilePath);
        if (response == null) {
            return null;
        }
        String strResult = response.getStringResult();

        return strResult;
    }

    /**
     * MAP类型数组转换成NameValuePair类型
     * @param properties  MAP类型数组
     * @return NameValuePair类型数组
     */
    private static NameValuePair[] generatNameValuePair(Map<String, String> properties) {
        NameValuePair[] nameValuePair = new NameValuePair[properties.size()];
        int i = 0;
        for (Map.Entry<String, String> entry : properties.entrySet()) {
            nameValuePair[i++] = new NameValuePair(entry.getKey(), entry.getValue());
        }

        return nameValuePair;
    }
    
    /**
     * 用于防钓鱼，调用接口query_timestamp来获取时间戳的处理函数
     * 注意：远程解析XML出错，与服务器是否支持SSL等配置有关
     * @return 时间戳字符串
     * @throws IOException
     * @throws DocumentException
     * @throws MalformedURLException
     */
	public static String query_timestamp() throws MalformedURLException,
                                                        DocumentException, IOException {

        //构造访问query_timestamp接口的URL串
        String strUrl = ALIPAY_GATEWAY_NEW + "service=query_timestamp&partner=" + AlipayConfig.partner + "&_input_charset" +AlipayConfig.input_charset;
        StringBuffer result = new StringBuffer();

        SAXReader reader = new SAXReader();
        Document doc = reader.read(new URL(strUrl).openStream());

        List<Node> nodeList = doc.selectNodes("//alipay/*");

        for (Node node : nodeList) {
            // 截取部分不需要解析的信息
            if (node.getName().equals("is_success") && node.getText().equals("T")) {
                // 判断是否有成功标示
                List<Node> nodeList1 = doc.selectNodes("//response/timestamp/*");
                for (Node node1 : nodeList1) {
                    result.append(node1.getText());
                }
            }
        }

        return result.toString();
    }
	
	/**
	 * 递归解析所有账单
	 * @param strResult
	 * @param sParaTemp
	 * @param billList
	 * @return
	 * @throws Exception
	 */
	@SuppressWarnings("unchecked")
	public static List<Map<String,String>> parseXml(String strResult, Map<String, String> sParaTemp, List<Map<String,String>> billList) throws Exception {
		// 将解析结果存储在HashMap中
		Map<String, String> map = new HashMap<String, String>();
		// 读取输入流
		SAXReader reader = new SAXReader();
		Document document = reader.read(new ByteArrayInputStream(strResult.getBytes("GBK")));
		// 得到xml根元素
		Element root = document.getRootElement();
		// 成功标志
		Element is_success = root.element("is_success");
		
		if(is_success !=null && is_success.getText().equals("T")){
			Element account_page_query_result = root.element("response").element("account_page_query_result");
			// 得到所有账单节点
			List<Element> accountQueryAccountLogVO = account_page_query_result.element("account_log_list").elements();
			// 遍历所有子节点
			for (Element e : accountQueryAccountLogVO){
				List<Element> property = e.elements();
				for(Element account : property){
					map.put(account.getName(), account.getText());
				}
				billList.add(map);
			}
			//如果有下一页，获取并递归解析
			if("T".equals(account_page_query_result.element("has_next_page").getTextTrim())){
				int page_no = Integer.parseInt(account_page_query_result.element("page_no").getText().trim());
				sParaTemp.put("page_no", String.valueOf(page_no+1));
				parseXml(buildRequest("","",sParaTemp),sParaTemp,billList);
			}
		}
		return billList;
	}
	
	public static List<Map<String,String>> getBills(String page_no, String gmt_start_time, String gmt_end_time){
		//把请求参数打包成数组
		Map<String, String> sParaTemp = new HashMap<String, String>();
		sParaTemp.put("service", "account.page.query");
		sParaTemp.put("partner", AlipayConfig.partner);
		sParaTemp.put("_input_charset", AlipayConfig.input_charset);
		sParaTemp.put("page_no", page_no);
		sParaTemp.put("gmt_start_time", gmt_start_time);
		sParaTemp.put("gmt_end_time", gmt_end_time);
		try {
			//账单队列
			List<Map<String,String>> billList = new ArrayList<Map<String,String>>();
			String sHtmlText = buildRequest("", "", sParaTemp);//获取第一页数据
			parseXml(sHtmlText,sParaTemp,billList);//解析xml，如果有下一页再获取并解析
			
			return billList;
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}
	/*
	public static void main(String args[]){
		//页号
		String page_no = "1";
		//必填，必须是正整数

		//账务查询开始时间
		String gmt_start_time = "2015-11-01 08:00:00";
		//格式为：yyyy-MM-dd HH:mm:ss

		//账务查询结束时间
		String gmt_end_time = "2015-11-07 08:00:00";
		//格式为：yyyy-MM-dd HH:mm:ss


		//////////////////////////////////////////////////////////////////////////////////

		//把请求参数打包成数组
		Map<String, String> sParaTemp = new HashMap<String, String>();
		sParaTemp.put("service", "account.page.query");
		sParaTemp.put("partner", AlipayConfig.partner);
		sParaTemp.put("_input_charset", AlipayConfig.input_charset);
		sParaTemp.put("page_no", page_no);
		sParaTemp.put("gmt_start_time", gmt_start_time);
		sParaTemp.put("gmt_end_time", gmt_end_time);

		try {
			//建立请求
			//String sHtmlText = AlipaySubmit.buildRequest("", "", sParaTemp);
			String sHtmlText = "<?xml version=\"1.0\" encoding=\"GBK\"?> <alipay> <is_success>T</is_success> <response> <account_page_query_result> <account_log_list><AccountQueryAccountLogVO><balance>21640857.99</balance> <bank_account_name>中国工商银行</bank_account_name > <bank_account_no>6228098765432109877</bank_account_no> <bank_name>张三</bank_name> <buyer_account>20881010118937730156</buyer_account> <currency>156</currency> <deposit_bank_no>1407872714662639</deposit_bank_no> <goods_title>商品1</goods_title> <income>0.00</income> <iw_account_log_id>340005462311</iw_account_log_id> <memo>交易付款</memo> <merchant_out_order_no>8914444063140954 </merchant_out_order_no> <other_account_email>other@alipay.com </other_account_email> <other_account_fullname>xx公司</other_account_fullname> <other_user_id>2088001368431897</other_user_id> <outcome>500.00</outcome> <partner_id>2088101118137074</partner_id> <seller_account>20881010118937520156</seller_account> <seller_fullname>xx商户</seller_fullname> <service_fee>1.00</service_fee> <service_fee_ratio>0.02</service_fee_ratio> <total_fee>30.00</total_fee> <trade_no>2012050726014177</trade_no> <trade_refund_amount>1.00</trade_refund_amount> <trans_account>20881010118937220156</trans_account> <trans_code_msg>转账</trans_code_msg> <trans_date>2010-09-25 17:27:30</trans_date> <trans_out_order_no>2012050726014177 </trans_out_order_no> <sub_trans_code_msg>冻结余额</sub_trans_code_msg> <sign_product_name>高级即时到账</sign_product_name> <rate>0.003</rate> </AccountQueryAccountLogVO> </account_log_list> <has_next_page>F</has_next_page> <page_no>1</page_no> <page_size>5000</page_size> </account_page_query_result> </response> <sign>326e59040fb8d21149daf43e88e4c12a</sign> <sign_type>MD5</sign_type> </alipay>";
			List<Map<String,String>> list = getBills("1",gmt_start_time,gmt_end_time);
			for(Map<String,String> map : list){
				Iterator<Map.Entry<String, String>> it = map.entrySet().iterator();
				  while (it.hasNext()) {
				   Map.Entry<String, String> entry = it.next();
				   System.out.println("key= " + entry.getKey() + " and value= " + entry.getValue());
				  }
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	*/
}
